//
//  OTAViewController.swift
//  Keyboard
//
//  Created by liujianlong on 2023/6/7.
//

import UIKit
import SwiftyJSON
struct firmwarePara{
    var fileName:String
    var fileSize:CGFloat
}
class OTAViewController: CCAlertViewController {
    @IBOutlet var bgView: UIView!
    @IBOutlet weak var num: UILabel!
    @IBOutlet weak var progress: UIProgressView!
    @IBOutlet var numLeft: NSLayoutConstraint!
    @IBOutlet var tips: UILabel!
    var bin = [UInt8]()
    var param : [String:Any]?
    var firmwareInfo:firmwarePara?
    var frist_address : String = ""
    var crc : Int = 0
    var sendData = [[[UInt8]]]()
    var page_4096 : Int {
        return page(4096)
    }
    
    var page_235 : Int {
        return page(235)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        addNotify()
        initUI()
    }
    
    func initUI(){
        tips.text = LS("请稍后")
        bgView.backgroundColor = "#000000".color(0.8)
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        guard let file = (param?["fileName"] as? JSON)?.stringValue else{return}
        var size = (param!["fileSize"] as? JSON)?.floatValue ?? 0
        firmwareInfo = firmwarePara(fileName:file, fileSize: CGFloat(size) * 1024.0)
        local()
    }
    
    class func show(_ param:[String:Any]?){
        let vc = OTAViewController()
        vc.modalPresentationStyle = .overFullScreen
        vc.param = param
        self.currentVC().present(vc, animated: false)
    }
    
    ///读取本地
    func local(){
        let url_str = EPApi.download(firmwareInfo!.fileName).localLocation.absoluteString
        let file = url_str[7...url_str.count]
        
        if FileManager.default.fileExists(atPath:file){
            guard let data = try? Data(contentsOf: URL(string: url_str)!) as? Data else{ return}
            bin = [UInt8](data)
            tips.text = LS("更新固件")
            ///读取起始位置
            BleDataTool.share.readStorageAddress()
            //            crc = getCRC32new()
            crc = Int(FRIBinFileCheck.crc32(forBinFile: Data(bin)))
            print("")
        }else{
            download()
        }
    }
    
    func page(_ length:Int)->Int{
        var count = bin.count / length
        if bin.count % length > 0{
            count += 1
        }
        return count
    }
    
    ///下载
    func download(){
        tips.text = LS("下载固件")
        NetworkTool.request(.download(firmwareInfo!.fileName)){[weak self] response in
            let url = response["fileURL"].stringValue
            print("下载地址是\(url)")
            self?.local()
        } _ : {[weak self] (total, complet) in
            let scale = CGFloat(complet) / CGFloat((self?.firmwareInfo!.fileSize)!)
            self?.setupProgress(scale)
        }
    }
    
    ///进度条
    func setupProgress(_ p:CGFloat){
        
        progress.progress = Float(p)
        num.text = "\(Int(progress.progress * 100))%"
        
        let s = (self.progress.frame.width - num.frame.width) / 100.0
        let value = CGFloat(self.progress.progress) * s * 100.0
        self.numLeft.constant = value
    }
    
    ///分包
    func subBin(){
        var i = 0
        while (i<page_4096){
            let sub_count = Int(ceil(4096.0 / 235.0))
            var j = 0
            var sub_bin = [[UInt8]]()
            while (j<sub_count){
                let s = i*4096+j*235
                let e = j*235 > sub_count ? i*4096+sub_count : s
                let temp = [UInt8](bin[s..<e])
                sub_bin.append(temp)
                j += 1
            }
            sendData.append(sub_bin)
            i += 1
        }
    }
    
    ///清除所有地址
    func clear(){
        var address = frist_address.toDecimal()
        for i in 0..<page_4096{
            address = frist_address.toDecimal() + 4096 * i
            BleDataTool.share.clearStorageAddress(address)
        }
    }
    
    ///写数据
    func kwrite(_ address:Int){
        let p = (address - frist_address.toDecimal()) / 235
        if p <= page_235 - 1{
            let s = p*235
            let e = p < page_235 - 1 ? p*235+235 : bin.count
            let temp = [UInt8](bin[s..<e])
            BleDataTool.share.updateOTA(address, temp)
            ///间隔0.001秒，写入下次数据，直接循环写，硬件会数据拥挤，收不完整
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.02) {[weak self] in
                self?.kwrite(address+235)
            }
        }
    }
}

extension  OTAViewController{
    func addNotify() {
        NotificationCenter.default.addObserver(self, selector: #selector(actionForFirstAddress), name:BLEFirstAddressResponse, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(actionForClearAddress), name:BLEClearAddressResponse, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(actionForWriteAddress), name:BLEWriteAddressResponse, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(actionForBLEStatusUpdate), name: NSNotification.Name(BLEStatusUpdateNotifyName), object: nil)
    }
    
    ///重新连接
    @objc func actionForBLEStatusUpdate(){
        self.dismiss(animated: false)
    }
    
    ///获取首地址回调
    @objc func actionForFirstAddress(_ notify:Notification){
        guard let address = notify.object as? String else{return}
        frist_address = address
        clear()
    }
    
    ///擦除数据回调
    @objc func actionForClearAddress(_ notify:Notification){
        guard let address = notify.object as? String else{return}
        let current_page = 1 + (address.toDecimal() - frist_address.toDecimal()) / 4096
        let scale = CGFloat(current_page) / CGFloat(page_4096)
        setupProgress(scale)
        if page_4096 == current_page{///擦除完毕，开始写入数据
            kwrite(frist_address.toDecimal())
        }
    }
    
    ///写入数据回调
    @objc func actionForWriteAddress(_ notify:Notification){
        guard let dict = notify.object as? [String:String] else{return}
        guard let address = dict["address"] else{return}
        guard let packet_len = dict["packet_len"] else{return}
        let current_page = 1 + (address.toDecimal() - frist_address.toDecimal()) / 235
        let scale = CGFloat(current_page) / CGFloat(page_235)
        setupProgress(scale)
        MyPrint("========写入数据\(address) 当前页数/总页数\(current_page)/\(page_235)")
        ///写入完毕，开始重启
        if page_235 == current_page{
            bgView.isHidden = true
            CCHud.start(LS("重启中"), stopTime: Int32(1)){[weak self] in
                let length  = (self?.bin)!.count
                BleDataTool.share.resetOTA(length, (self?.crc)!)
            }
        }
    }
    
}


// MARK: - 计算CRC
extension OTAViewController{
    func calculateCRC(_ data: Data) -> Int{
        let bytes = [UInt8](data)
        var crc: Int = 0
        
        for item in bytes {
            crc = CRC32_Table()[(crc ^ Int(item)) & 0xff] ^ (crc >> 8)
        }
        let b = crc ^ 0xffffffff
        return b
    }
    
    func calculateCRC32(data: [UInt8], _ c:Int) -> Int {
        var crc: UInt32 = UInt32(c)
        
        for byte in data {
            let tableIndex = Int((crc ^ UInt32(byte)) & 0xFF)
            crc = (crc >> 8) ^ UInt32(CRC32_Table()[tableIndex])
        }
        
        let result = crc ^ 0xFFFFFFFF
        return Int(result)
    }
    
    ////CRC32校验
    func crc32CalByByte(oldcrc: Int, ptr: [UInt8], offset: Int, len: Int) -> Int {
        var crc: Int = oldcrc
        var i = offset
        for _ in 0..<len {
            let high = (crc >> 24) & 0xFF ///取高8位
            crc <<= 8
            crc ^= CRC32_Table()[(high ^ Int(ptr[i])) & 0xFF]
            crc &= 0xFFFFFFFF
            i += 1
        }
        
        return crc & 0xFFFFFFFF
    }
    
    func getCRC32new()-> Int {
        ///截掉首个256字节
        let b = [UInt8](bin[256..<bin.count])
        
        ///计算有多少个256字节，剩余不足256，也加1
        var count = b.count / 256
        if b.count % 256 > 0{
            count += 1
        }
        
        var crcInit: Int = 0
        var counts = 0
        
        ///对每个256字节进行crc校验，
        for i in 0..<count{
            let start = i * 256
            let end = (i + 1) * 256
            var n_b = [UInt8]()
            if b.count >= end{
                n_b = [UInt8](b[start..<end])
            }else{
                ///对最后一个小包，进行crc校验
                n_b = [UInt8](b[start..<b.count])
            }
            crcInit = crc32CalByByte(oldcrc: crcInit, ptr: n_b, offset: 0, len: n_b.count)
            counts += 1
            print("count: \(counts), read: \(n_b.count), crcInit: \(String(format: "%08X", crcInit))")
        }
        return crcInit
    }
    
    /* CRC 字节余式表 */
    func CRC32_Table()->[Int] {
        let byte = [
            0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
            0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
            0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
            0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
            0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
            0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
            0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
            0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
            0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
            0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
            0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
            0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
            0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
            0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
            0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
            0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
            0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
            0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
            0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
            0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
            0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
            0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
            0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
            0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
            0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
            0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
            0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
            0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
            0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
            0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
            0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
            0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
        ]
        return byte
    }
}
